<template>
    <div class="rootDiv">
        <!--上传成功之后的转换任务列表-->
        <el-table  :data="tableData" tooltip-effect="dark" style="width: 100%">
            <el-table-column prop="fileName" label="文件名"></el-table-column>
            <el-table-column prop="fileSize" label="大小"></el-table-column>
            <el-table-column label="处理状态">
                <template slot-scope="scope">
                    <span>{{ scope.row.taskStateDesc }}</span>
                </template>
            </el-table-column>
            <el-table-column prop="ruleId" label="操作" width="300">
                <template slot-scope="scope">
                    <el-button @click="onExchange(scope.row)" type="text" >发起转换</el-button>
                    <el-button @click="onShow(scope.row)" type="text" >查看</el-button>
                    <el-button @click="onDelete(scope.row)" type="text" ><span  id="deleteButton">删除</span></el-button>
                </template>
            </el-table-column>
        </el-table>
        <!--上传文件控件-->

        <el-row type="flex" justify="start">
            <el-upload class="upload-demo" :action="this.bimiServer"
                ref="upload"
                :multiple="false"
                :headers="token"
                :before-upload="onBeforeUpload"
                :on-success="onUploadSuccess"
                :on-error="onUploadError">
                <!-- <el-button  style="margin-left: 10px;" @click="onBtnDirClick">{{this.ifc.dirName}}</el-button> -->
                <el-button slot="trigger" type="primary" icon="el-icon-upload" >上传我的文件</el-button>
		    </el-upload>
        </el-row>
        <!--查询服务器待处理任务的数量-->
        <el-row style="margin-top:5px">
            <el-button @click="onQueryWaiting()">查询待处理任务</el-button>
        </el-row>
        <!--发起转换后弹出的导出参数对话框-->
        <el-dialog title="导出设置" :visible.sync="exchangeDialogVisible" width="400px" :close-on-click-modal="false">
			<el-form ref="form" :model="exchangeTask"  label-width="100px" >
                <el-row>
                    <el-form-item label="主文件" v-if="exchangeTask.isZip">
                        <el-select v-model="linkMain" placeholder="请选择" style="width:100%">
                                <el-option
                                    v-for="item in linkFiles"
                                    :key="item.value"
                                    :label="item.label"
                                    :value="item.value">
                                </el-option>
                        </el-select>
                    </el-form-item>
                    <el-form-item label="模型精度" >
                        <el-select v-model="exchangeTask.level" placeholder="请选择" style="width:100%">
                                <el-option
                                    v-for="item in levels"
                                    :key="item.value"
                                    :label="item.label"
                                    :value="item.value">
                                </el-option>
                        </el-select>
                    </el-form-item>
                    <el-form-item label="渲染模式" >
                        <el-select v-model="exchangeTask.renderMode" placeholder="请选择" style="width:100%">
                                <el-option
                                    v-for="item in renderMode"
                                    :key="item.value"
                                    :label="item.label"
                                    :value="item.value">
                                </el-option>
                        </el-select>
                    </el-form-item>
                    <el-form-item label="导出目录树" >
                        <el-checkbox v-model="exchangeTask.tree"></el-checkbox>
                    </el-form-item>
                    <el-form-item label="导出构件属性" >
                        <el-checkbox v-model="exchangeTask.property"></el-checkbox>
                    </el-form-item>
                    <el-form-item label="视图设置" >
                        <el-radio v-model="exchangeTask.customView" label="0">默认</el-radio>
                        <el-radio v-model="exchangeTask.customView" label="1">自定义</el-radio>
                        <el-input v-model="exchangeTask.viewName" placeholder="请输入需要转换的三维视图名称" v-show="exchangeTask.customView === '1'"></el-input>
                    </el-form-item>
                </el-row>
			</el-form>
			<span slot="footer" class="dialog-footer">
				<el-button @click="dialogVisible = false">取 消</el-button>
				<el-button type="primary" @click="startExchange">确 定</el-button>
			</span>
		</el-dialog>
       
    </div>
</template>
<script>
import requestBimi from './requestBimi';//封装了请求bimi服务器的axios,增加了http header,可以参考
export default {
    name:"UploadRvt",
    data(){
        return {
            //bimi服务器,其实就是FileServer
            bimiServer:'http://127.0.0.1:9999',
            //BIMI服务端防攻击token
            token:{
               Authorization:"bimi"
            },
            //当前上传文件后获得的任务id
            curTaskId:null,
            tableData:[],//上传列表
            tasks:[],//任务列表，真实项目中应该存储到数据库中
            //发起转换的任务信息
            exchangeDialogVisible:false,
            exchangeTask:{
                isZip:false,//是否是zip文件
                level:'6',
                tree:false,
                property:false,
                renderMode:'real',
                taskId:null,//任务ID，这个id也是上传文件后的taskId，这是一个guid，贯穿从文件上传到转换完成的全生命周期
                name:null,//文件名称
                path:null,//文件路径
                customView:"0",//自定义导出视图
                viewName:null,//导出视图，null为默认{3D}视图
            },
            linkMain:null,//链接模型zip包中的主文件
            linkFiles:[],
            levels:[
                {value: '1',label: '粗略'},
                {value: '3',label: '简单'},
                {value: '6',label: '中等'},
                {value: '8',label: '精细'},
            ],
            renderMode:[
                {value:'real',label:'真实'},
                {value:'color',label:'着色'}
            ],
        }
    },
    methods:{
        //上传开始前的钩子
        onBeforeUpload(file){
            //文件格式判断
            const isRvt = (
                file.name.endsWith(".rvt")
                ||file.name.endsWith(".rfa")
                ||file.name.endsWith(".zip") 
                ||file.name.endsWith(".ifc")
                ||file.name.endsWith(".IFC")
            );
            if (!isRvt) 
            {
                this.$message.error('上传文件只能是 rvt、rfa、zip、ifc 格式!');
                return false;
            }
            return true;
        },
        onUploadSuccess(response, file, fileList) {
            //上传成功后，会返回转换任务的taskId，这个是发起转换和查询转换状态的唯一凭证
            this.curTaskId = response.taskId;
            //清除VUE上传控件默认显示的上传文件列表
            this.$refs.upload.clearFiles();
            this.pollingUploadState();//轮询上传状态
        },
        onUploadError(err, file, fileList){
            this.$notify({
                title: '消息',
                message: '文件上传失败,请检查bimi服务器是否运行',
                type: 'error',
                duration:3000
            });
        },
        //轮询上传状态，这是因为上传zip文件，服务端解压缩是异步执行的，所以需要轮询确认上传解压完成，才能发起转换任务
        pollingUploadState(){
            //2秒一次，查询上传解压进度，获得Task对象
            this.uploadInterval = setInterval(()=>{
                //Get请求，action是queryUpload
                let taskId = this.curTaskId;
                if(taskId)
                {
                    requestBimi.get(this.bimiServer,{ params: {action:"queryUpload",taskId:taskId} }).then(data=>{
                        this.onUploaded(data);
                    })
                }
            },2000);
        },
         //确认上传文件成功之后的操作
        onUploaded(data){
            let complete = data.complete;//是否上传完成的标志
            if(complete)
            {
                //清除定时器
                if(this.uploadInterval)
                {
                    clearInterval(this.uploadInterval);
                }
                let task = data.task;//获得任务信息
                //保存任务，真实项目中，应该保存到数据库中同步
                this.tasks.push(task);
                //在tableData增加一行数据，真实项目中，可以把任务信息添加到数据库中，以便持久化保存信息
                this.tableData.push({
                    taskId:task.taskId,//上传任务ID
                    fileName:task.fileName,//文件名称
                    path:task.path,//文件保存的磁盘目录
                    fileSize:task.fileSize,//文件大小(M单位，小数点2位)
                    taskStateDesc:'上传完毕，等待转换',//任务状态
                })
                //console.info(task);
            }
        },

        //弹出转换参数设置对话框
        onExchange(row){
            let uploadTaskId = row.taskId;//获得任务id，该id将贯穿整个转换过程中,可用该id发起转换操作，在真实项目中，这些信息应保存到你的数据库中
            //我这里不依赖数据库，所以直接从缓存的tasks中遍历查询需要的数据
            let task = null;
            this.tasks.forEach(item=>{
                if(item.taskId === uploadTaskId)
                {
                    task = item;
                }
            })
            //找到上传任务后，表示该上传任务已经完成,根据上传任务中的信息弹出导出对话框上
            if(task)
            {
                //根据任务信息，创建exchangeTask信息
                this.exchangeTask.taskId = task.taskId;
                this.exchangeTask.isZip = task.isZip;
                this.exchangeTask.name = task.fileName;//文件名称
                //非zip，单个文件，那么需要转换的文件夹就是fileInfo
                if(task.fileInfo)
                {
                    this.exchangeTask.path = task.fileInfo.path;//文件在服务器上的磁盘路径
                }
                //如果是zip压缩文件，那么需要让用户选择主文件,文件名称和路径在点击对话框的'确定'按钮后才能确定
                if(task.isZip)
                {
                    this.linkFiles.length = 0;//清空上一次的zip中包含的文件列表
                    //添加该上传文件zip中包含的所有文件，让用户选择主文件
                    task.zipInfo.files.forEach(item=>{
                        this.linkFiles.push({label:item.name,value:item.path})
                    })
                }
                //弹出对话框
                this.exchangeDialogVisible = true;
            }
        },
        //发起转换任务
        startExchange(){
            //获得对话框数据，确定好转换参数
            if(this.exchangeTask.isZip)
            {
                if(this.linkMain == null)
                {
                    this.$message.error('链接模型需选择主文件');
                }
                this.exchangeTask.path = this.linkMain;
            }
            //关闭对话框
            this.exchangeDialogVisible = false;
            this.linkMain = null;
            //发起转换任务之前，最好把文件path记录到数据库中，这样在删除文件的时候，可以用path作为请求参数

            //对path进行编码，防止中文和特殊字符乱码
            let encodePath = encodeURIComponent(this.exchangeTask.path);
            // 转换请求的url拼接，参数包含taskId、path、level、tree、property、renderMode
            let url = this.bimiServer + "?action=exchange&taskId="+this.exchangeTask.taskId+"&path="+encodePath+"&level="+this.exchangeTask.level+"&tree="+this.exchangeTask.tree+"&property="+this.exchangeTask.property+"&renderMode="+this.exchangeTask.renderMode;
            //自定义视图
            if(this.exchangeTask.viewName!= null && this.exchangeTask.viewName.trim() !== '')
            {
                let viewName = encodeURIComponent(this.exchangeTask.viewName);
                url = url + "&viewName="+viewName;
            }
            console.info(url);
            //发起转换请求，注意转换在服务端是异步执行的，这里收到的回应只是针对发起转换请求的回应
            requestBimi.get(url).then(data=>{
                if(data.success)//发起转换请求成功，此时转换服务器正在进行转换工作
                {
                    console.info(data);
                    /*
                    成功的返回数据，exchangeServer是转换服务器的地址，taskId是js传过去的任务号，后面可以根据这个taskId查询转换任务是否完成
                    { "success", true },
                    { "exchangeServer",server},
                    { "taskId",task.taskId}
                    */
                    
                    //更新前端表格中的文件状态，以下代码只是模拟，真实项目中应该从数据库中查询确保数据一致性
                    this.tableData.forEach(item=>{
                        if(item.taskId === this.exchangeTask.taskId)// upload后收到的taskId，会保持整个转换生命周期一致性和唯一性。
                        {
                            item.taskStateDesc = "正在转换...";
                        }
                    })
                    this.pollingExchangeState(this.exchangeTask.taskId);//轮询转换状态是否完成
                }
                else
                {
                    console.error(data.error);//如果失败，服务端会传来异常信息，注意这个失败是发起转换请求失败，并不是转换过程中的失败
                }
            })
            
           
        },
        //轮询转换是否完成，我这里演示为了不依赖环境，所以采用了web端轮询转换状态的方式,这种方式并不推荐，因为会对bimi服务器造成性能压力
        //实际项目中，推荐采用回调接口的方式，bimi服务器的config.xml中可以设置一个服务器回调接口，
        //在转换完成或转换失败后都会实时通过该接口通知业务系统。
        pollingExchangeState(taskId){
            //2秒一次，查询转换是否完成，获得转换后的信息对象
            this.exchangeInterval = setInterval(()=>{
                //Get请求，action是queryExchange
                if(taskId)
                {
                    requestBimi.get(this.bimiServer,{ params: {action:"queryExchange",taskId:taskId} }).then(data=>{
                        //返回的数据格式
                        /**
                         * {
                         *     "taskId":string类型，唯一任务id
                         *     "success":bool类型，是否转换成功
                         *     "error":string类型，如果失败，可能会带的异常信息
                         *     "url":string类型，可以从web端访问的转换后轻量化数据的url，只有sucess为true时，该属性才有值
                         *     "startTime":string类型,转换开始时间格式化文本,格式化为yyyy-MM-dd HH:mm:ss,只有sucess为true时，该属性才有值
                         *     "endTime":同上，转换结束时间,只有sucess为true时，该属性才有值
                         *     "path":string类型,被转换的文件，注意如果是zip压缩链接文件，那么是主文件路径
                         * }
                         */
                        if(data.success)
                        {
                            //停止轮询
                            if(this.exchangeInterval)
                            {
                                clearInterval(this.exchangeInterval);
                            }
                            let taskId = data.taskId;;
                            //我这里就修改下table中这条记录的状态，并且把返回的可访问url设置上
                            this.tableData.forEach(item=>{
                                if(item.taskId === this.exchangeTask.taskId)// upload后收到的taskId，会保持整个转换生命周期一致性和唯一性。
                                {
                                    item.taskStateDesc = "转换完成";
                                    item.url = data.url;
                                }
                            })
                        }
                    })
                }
              
            },2000);
        },

        onShow(row){
            if(row.url)
            {
                this.$notify({
                    title: '演示',
                    message: '可以通过以下url加载bim:'+row.url,
                    type: 'info',
                    duration:3000
                });
            }
        },
        onDelete(row){
            if(row.path)
            {
                //对path路径进行url编码，防止中文和特殊字符乱码问题
                let encodePath = encodeURIComponent(row.path);
                //action=deleteFile
                let url = this.bimiServer + "?action=deleteFile&path="+encodePath;
            
                //发起删除文件的请求
                requestBimi.get(url).then(data=>{
                    if(data.success)//发起删除文件请求，返回的数据对象中的success表示删除成功/失败
                    {
                        this.$notify({
                            title: '演示',
                            message: '删除成功',
                            type: 'info',
                            duration:3000
                        });
                        //刷新表格，真实项目中，应该刷新数据库并同步UI
                        let deleteIndex = -1;
                        for(var i=0;i<this.tableData.length;i++)
                        {
                            if(this.tableData[i].taskId === row.taskId)
                            {
                                deleteIndex = i;
                                break;
                            }
                        }
                        this.tableData.splice(deleteIndex,1);
                    }
                    else
                    {
                        console.error("文件删除失败,请检查文件路径是否存在");//如果失败，服务端会传来异常信息
                    }
                })

               
            }
        },
       
        //查询某个服务器上的待处理任务，可用于多服务器集群模式下去寻找待处理任务最少的
        onQueryWaiting(){
            //查询待处理任务的http url
            let url = this.bimiServer + "?action=queryWating";
            
            //发起请求
            requestBimi.get(url).then(data=>{
                if(data)
                {
                    console.info(data);
                }    
            })
        },

        test(){
           let data = {
                isZip: false,
                level: "8",
                tree: true,
                property: true,
                renderMode: "real",
                taskId: "6fcf521a-c051-4bb4-8d38-a067843a9c14",
                name: "机柜.rvt",
                path: "D:\\RevitPlugins\\store\\2022-3\\机柜\\机柜.rvt"
                //path:"D:\\RevitPlugins\\store\\2022-3\\链接模型测试\\链接模型测试\\主模型 方柱.rvt"
            }
            //对path进行编码，防止中文和特殊字符乱码
            let encodePath = encodeURIComponent(data.path);

            let url = this.bimiServer + "?action=exchange&taskId="+data.taskId+"&path="+encodePath+"&level="+data.level+"&tree="+data.tree+"&property="+data.property+"&renderMode="+data.renderMode;
            //发起转换请求
            requestBimi.get(url).then(data=>{
                if(data.success)//发起转换请求成功，此时转换服务器正在进行转换工作
                {
                    console.info(data);
                    /*
                    成功的返回数据，exchangeServer是转换服务器的地址，taskId是js传过去的任务号，后面可以根据这个taskId查询转换任务是否完成
                    { "success", true },
                    { "exchangeServer",server},
                    { "taskId",task.taskId}
                    */
                }
                else
                {
                    console.error(data.error);//如果失败，服务端会传来异常信息
                }
            })
       }
    },

    mounted() {
            
    },
    beforeDestroy(){
         //释放资源
        if(this.uploadInterval)
        {
            clearInterval(this.uploadInterval);
        }
        if(this.exchangeInterval)
        {
            clearInterval(this.exchangeInterval);
        }
    }
  
}
</script>
<style scoped>
.rootDiv {
    position: relative; 
    width: 100%;
    height: 100%;
    padding: 0px;
    overflow: auto;
}
</style>


